﻿using System;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Xml.Serialization;

namespace MathService.Vectors
{
    /// <summary>Двумерный вектор</summary>
    [Serializable]
    [TypeConverter(typeof(Vector2DConverter))]
    public struct Vector2D : IEquatable<Vector2D>, ICloneable<Vector2D>
    {
        /// <summary>Координата X</summary>
        private double f_X;
        /// <summary>Координата Y</summary>
        private double f_Y;

        /// <summary>Координата X</summary>
        [XmlAttribute]
        public double X { get { return f_X; } set { f_X = value; } }

        /// <summary>Координата Y</summary>
        [XmlAttribute]
        public double Y { get { return f_Y; } set { f_Y = value; } }

        /// <summary>Радиус (длина) вектора</summary>
        [XmlIgnore]
        public double R
        {
            set
            {
                var arg = Angle;
                var abs = value;
                f_X = abs * Math.Cos(arg);
                f_Y = abs * Math.Sin(arg);
            }
            get { return Math.Sqrt(f_X * f_X + f_Y * f_Y); }
        }

        /// <summary>Угол к оси X в радианах</summary>
        [XmlIgnore]
        public double Angle
        {
            get
            {
                return f_X.Equals(0)
                    ? (f_Y.Equals(0)
                        ? 0
                        : Math.Sign(f_Y) * Consts.pi05)
                    : (f_Y.Equals(0)
                        ? (Math.Sign(f_X) > 0
                            ? 0
                            : Consts.pi)
                        : Math.Atan2(f_Y, f_X));
            }
            set
            {
                var arg = value;
                var abs = R;
                f_X = abs * Math.Cos(arg);
                f_Y = abs * Math.Sin(arg);

            }
        }

        /// <summary>Инициализация двумерного вектора</summary>
        /// <param name="X">Координата X</param>
        /// <param name="Y">Координата Y</param>
        public Vector2D(double X, double Y)
        {
            f_X = X;
            f_Y = Y;
        }

        /// <summary>Инициализация вектора по по комплексному числу</summary>
        /// <param name="Z">Комплексное число X + iY</param>
        private Vector2D(Complex Z)
        {
            f_X = Z.Re;
            f_Y = Z.Im;
        }

        /// <summary>Представление вектора в базисе</summary>
        /// <param name="b">Базис</param>
        /// <returns>Вектор в базисе</returns>
        public Vector2D InBasis(Basis2D b)
        {
            return new Vector2D(b.xx * f_X + b.xy * f_Y, b.yx * f_X + b.yy * f_Y);
        }

        public override string ToString() { return string.Format("({0};{1})", X, Y); }

        public Vector2D Clone() { return new Vector2D(f_X, f_Y); }

        object ICloneable.Clone() { return Clone(); }

        public bool Equals(Vector2D other) { return f_X.Equals(other.f_X) && f_Y.Equals(other.f_Y); }

        public override bool Equals(object obj) { return obj is Vector2D && Equals((Vector2D)obj); }


        public override int GetHashCode() { unchecked { return (f_X.GetHashCode() * 0x18d) ^ f_Y.GetHashCode(); } }

        #region Операторы

        public static bool operator ==(Vector2D a, Vector2D b) { return a.Equals(b); }
        public static bool operator !=(Vector2D a, Vector2D b) { return !(a == b); }

        public static explicit operator Complex(Vector2D P) { return new Complex(P.X, P.Y); }
        public static explicit operator Vector2D(Complex Z) { return new Vector2D(Z); }

        public static Vector2D operator +(Vector2D a) { return new Vector2D(a.X, a.Y); }
        public static Vector2D operator -(Vector2D a) { return new Vector2D(-a.X, -a.Y); }

        public static double operator *(Vector2D a, Vector2D b) { return a.X * b.X + a.Y * b.Y; }

        public static Vector2D operator +(Vector2D a, double b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, double b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, double b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, double b)
        {
            Contract.Requires(!b.Equals(0), "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(double a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(double a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(double a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(double a, Vector2D b)
        {
            Contract.Requires(!b.X.Equals(0), "b.X == 0");
            Contract.Requires(!b.Y.Equals(0), "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        public static double operator ^(Vector2D a, Vector2D b) { return (a * b) / (a.R * b.R); }
        public static bool operator |(Vector2D a, Vector2D b) { return (a ^ b).Equals(1); }
        public static bool operator &(Vector2D a, Vector2D b) { return (a ^ b).Equals(0); }
        public static double operator %(Vector2D a, Vector2D b)
        {
            Contract.Requires(!b.R.Equals(0), "b.R == 0");
            return (a * b) / b.R;
        }
        public static Vector2D operator %(Vector2D a, Basis2D b) { return a.InBasis(b); }

        public static Vector2D operator +(Vector2D a, float b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, float b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, float b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, float b)
        {
            Contract.Requires(!b.Equals(0), "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(float a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(float a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(float a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(float a, Vector2D b)
        {
            Contract.Requires(!b.X.Equals(0), "b.X == 0");
            Contract.Requires(!b.Y.Equals(0), "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        public static Vector2D operator +(Vector2D a, int b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, int b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, int b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, int b)
        {
            Contract.Requires(!b.Equals(0), "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(int a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(int a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(int a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(int a, Vector2D b)
        {
            Contract.Requires(!b.X.Equals(0), "b.X == 0");
            Contract.Requires(!b.Y.Equals(0), "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        #endregion


    }
}
